//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.collaboration.port.adapter.persistence.view;

import com.saasovation.collaboration.domain.model.forum.*;
import com.saasovation.common.event.sourcing.DispatchableDomainEvent;
import com.saasovation.common.event.sourcing.EventDispatcher;
import com.saasovation.common.port.adapter.persistence.AbstractProjection;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.util.List;
import java.util.Map;

public class MySQLForumProjection extends AbstractProjection
        implements EventDispatcher {

    private final NamedParameterJdbcTemplate jdbcTemplate;

    private static final Class<?>[] understoodEventTypes = {
            ForumClosed.class,
            ForumDescriptionChanged.class,
            ForumModeratorChanged.class,
            ForumReopened.class,
            ForumStarted.class,
            ForumSubjectChanged.class
    };

    public MySQLForumProjection(EventDispatcher aParentEventDispatcher,
                                NamedParameterJdbcTemplate jdbcTemplate) {
        super();
        this.jdbcTemplate = jdbcTemplate;
        aParentEventDispatcher.registerEventDispatcher(this);
    }

    @Override
    public void dispatch(DispatchableDomainEvent aDispatchableDomainEvent) {
        this.projectWhen(aDispatchableDomainEvent);
    }

    @Override
    public void registerEventDispatcher(EventDispatcher anEventDispatcher) {
        throw new UnsupportedOperationException("Cannot register additional dispatchers.");
    }

    @Override
    public boolean understands(DispatchableDomainEvent aDispatchableDomainEvent) {
        return this.understandsAnyOf(
                aDispatchableDomainEvent.domainEvent().getClass(),
                understoodEventTypes);
    }

    protected void when(ForumClosed anEvent) {
        String sql = "update tbl_vw_forum " +
                "set closed = 1 " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.forumId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(ForumDescriptionChanged anEvent) {
        String sql = "update tbl_vw_forum " +
                "set description = :description " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("description", anEvent.description())
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.forumId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(ForumModeratorChanged anEvent) {
        String sql = "update tbl_vw_forum " +
                "set moderator_email_address = :moderatorEmail, " +
                "    moderator_identity = :moderatorIdentity, " +
                "    moderator_name = :moderatorName " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("moderatorEmail", anEvent.moderator().emailAddress())
                .addValue("moderatorIdentity", anEvent.moderator().identity())
                .addValue("moderatorName", anEvent.moderator().name())
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.forumId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(ForumReopened anEvent) {
        String sql = "update tbl_vw_forum " +
                "set closed = 0 " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.forumId().id());
        jdbcTemplate.update(sql, params);
    }

    protected void when(ForumStarted anEvent) {
        String existsSql = "select forum_id from tbl_vw_forum " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource existsParams = new MapSqlParameterSource()
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.forumId().id());
        List<Map<String, Object>> result = jdbcTemplate.queryForList(existsSql, existsParams);
        // idempotent operation
        if (!result.isEmpty()) {
            return;
        }
        String insertSql = "insert into tbl_vw_forum( " +
                "forum_id, closed, " +
                "creator_email_address, creator_identity, creator_name, " +
                "description, exclusive_owner, " +
                "moderator_email_address, moderator_identity, moderator_name, " +
                "subject, tenant_id" +
                ") values(" +
                ":forumId, :closed, " +
                ":creatorEmail, :creatorIdentity, :creatorName, " +
                ":description, :exclusiveOwner, " +
                ":moderatorEmail, :moderatorIdentity, :moderatorName, " +
                ":subject, :tenantId)";
        MapSqlParameterSource insertParams = new MapSqlParameterSource()
                .addValue("forumId", anEvent.forumId().id())
                .addValue("closed", 0)
                .addValue("creatorEmail", anEvent.creator().emailAddress())
                .addValue("creatorIdentity", anEvent.creator().identity())
                .addValue("creatorName", anEvent.creator().name())
                .addValue("description", anEvent.description())
                .addValue("exclusiveOwner", anEvent.exclusiveOwner())
                .addValue("moderatorEmail", anEvent.moderator().emailAddress())
                .addValue("moderatorIdentity", anEvent.moderator().identity())
                .addValue("moderatorName", anEvent.moderator().name())
                .addValue("subject", anEvent.subject())
                .addValue("tenantId", anEvent.tenant().id());
        jdbcTemplate.update(insertSql, insertParams);
    }

    protected void when(ForumSubjectChanged anEvent) {
        String sql = "update tbl_vw_forum " +
                "set subject = :subject " +
                "where tenant_id = :tenantId and forum_id = :forumId";
        MapSqlParameterSource params = new MapSqlParameterSource()
                .addValue("subject", anEvent.subject())
                .addValue("tenantId", anEvent.tenant().id())
                .addValue("forumId", anEvent.forumId().id());
        jdbcTemplate.update(sql, params);
    }
}
